package com.xuecheng.media.service.jobhandler;

import com.xuecheng.base.utils.Mp4VideoUtil;
import com.xuecheng.media.model.po.MediaProcess;
import com.xuecheng.media.service.IMediaProcessService;
import com.xuecheng.media.service.MediaFileService;
import com.xuecheng.media.service.impl.MediaProcessServiceImpl;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * XxlJob开发示例（Bean模式）
 *
 * 开发步骤：
 *      1、任务开发：在Spring Bean实例中，开发Job方法；
 *      2、注解配置：为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")"，注解value值对应的是调度中心新建任务的JobHandler属性的值。
 *      3、执行日志：需要通过 "XxlJobHelper.log" 打印执行日志；
 *      4、任务结果：默认任务结果为 "成功" 状态，不需要主动设置；如有诉求，比如设置任务结果为失败，可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果；
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Component
@Slf4j
public class SampleXxlJob {

     @Autowired
     IMediaProcessService mediaProcessService;

    @Autowired
    MinioClient minioClient;


    @Autowired
    MediaFileService mediaFileService;





    //存储视频
    @Value("${minio.bucket.videofiles}")
    private String bucket;
    //ffmpeg的路径
    @Value("${videoprocess.ffmpegpath}")
    private String ffmpeg;

    private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);
    /**
     * 1、简单任务示例（Bean模式）
     */
    @XxlJob("testJob")
    public void testJob() throws Exception {
        log.info("开始执行.....");

    }


//
//    /**
//     * 1、简单任务示例（Bean模式）
//     */
//    @XxlJob("demoJobHandler")
//    public void demoJobHandler() throws Exception {
//        XxlJobHelper.log("XXL-JOB, Hello World.");
//
//        for (int i = 0; i < 5; i++) {
//            XxlJobHelper.log("beat at:" + i);
//            TimeUnit.SECONDS.sleep(2);
//        }
//        // default success
//    }
//
//
    /**
     * 2、分片广播任务
     */
    @XxlJob("shardingJobHandler")
    public void shardingJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();  //执行器分片序号
        int shardTotal = XxlJobHelper.getShardTotal();  //执行器分片总数
        System.out.println("分片参数：当前分片序号 = " + shardIndex + ", 总分片数 = " + shardTotal);
        log.info("分片参数：当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);


    }

    @XxlJob("videoJobHandler")
    public void videoJobHandler() throws Exception {
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();  //执行器分片序号
        int shardTotal = XxlJobHelper.getShardTotal();  //执行器分片总数
        log.info("分片参数：当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
        //确定cpu的核数 代表可以并发处理的任务数
        int cpuCore = Runtime.getRuntime().availableProcessors();
        //查询待处理的任务
        List<MediaProcess> mediaProcessList = mediaProcessService.getMediaProcessList(shardIndex, shardTotal, cpuCore);
        if (mediaProcessList == null || mediaProcessList.size() == 0) {
            //没有待处理的任务
            log.info("没有待处理的任务");
            return;
        }
        //创建一个线程池

        //计数器
        CountDownLatch countDownLatch = new CountDownLatch(mediaProcessList.size());
        ExecutorService executorService = Executors.newFixedThreadPool(mediaProcessList.size());
        mediaProcessList.forEach(mediaProcess -> {

            //将每个任务添加到线程池
          try {
              executorService.execute(() -> {
                  //开启任务
                  boolean startTask = mediaProcessService.startTask(mediaProcess.getId());
                  if (!startTask) {
                      //开启任务失败
                      log.info("抢占任务失败，任务id:{}", mediaProcess.getId());
                      return;
                  }
                  //临时文件
                  File minioFile = null;
                  //创建一个临时存储mp4的文件            后续记得删除这个临时文件
                  File mp4File = null;

                  //从minio下载视频

                  FileOutputStream outputStream = null;
                  String objectName = mediaProcess.getFilePath(); //对象名也是存储路径
                  String fileId = mediaProcess.getFileId(); //原始md5

                  try {
                      InputStream stream = minioClient.getObject(GetObjectArgs.builder()
                              .bucket(bucket)
                              .object(objectName)
                              .build());
                      //创建临时文件
                      minioFile = File.createTempFile("minio", ".merge");       //将来要删除临时文件
                      outputStream = new FileOutputStream(minioFile);
                      IOUtils.copy(stream, outputStream);
                  } catch (Exception e) {
                      e.printStackTrace();
                      log.debug("minio下载源文件文件失败,originalFile:{}", mediaProcess.getBucket().concat(mediaProcess.getFilePath()));
                  } finally {
                      if (outputStream != null) {
                          try {
                              outputStream.close();
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                      }
                  }
                  //下载文件失败
                  if (minioFile == null) {
                      log.debug("下载待处理文件失败,originalFile:{}", mediaProcess.getBucket().concat(mediaProcess.getFilePath()));
                      mediaProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "下载待处理文件失败");
                      return;
                  }
                  //执行视频转码
                  //ffmpeg的路径
                  String ffmpeg_path = ffmpeg;//ffmpeg的安装位置
                  //源avi视频的路径
                  String video_path = minioFile.getAbsolutePath();
                  //转换后mp4文件的名称
                  String mp4_name = fileId + ".mp4";

                  try {
                      mp4File = File.createTempFile("minio", ".mp4");
                  } catch (IOException e) {
                      log.debug("创建临时文件失败{}", e.getMessage());
                      //保存任务处理失败的结果
                      mediaProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "创建临时mp4文件失败");
                      return;
                  }
                  //转换后mp4文件的路径
                  String mp4_path = mp4File.getAbsolutePath();
                  //创建工具类对象
                  Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpeg_path, video_path, mp4File.getName(), mp4_path);
                  //开始视频转换，成功将返回success
                  //开始视频转换，成功将返回success
                  String s = videoUtil.generateMp4();
                  if (!s.equals("success")) {
                      //视频转换失败
                      log.debug("视频转换失败,mp4File:{}，bucket{}，原因{}", mp4File.getAbsolutePath(), mediaProcess.getBucket(), s);
                      //保存任务处理失败的结果
                      mediaProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "视频转换失败");
                      return;
                  }
                  //mp4在minio的存储路径
                  String objectName1 = getFilePath(fileId, ".mp4");
                  //访问url
                  String url = "/" + bucket + "/" + objectName1;
                  //上传到minino
                  try{
                      mediaFileService.addMediaFilesToMinIO(mp4_path, "video/mp4", bucket,objectName1 );
                      //将url存储至数据，并更新状态为成功，并将待处理视频记录删除存入历史
                      mediaProcessService.saveProcessFinishStatus(mediaProcess.getId(), "2", fileId, url, null);
                  }catch (Exception e){
                      log.error("上传视频失败,mp4File:{}，bucket{}，原因{}", mp4File.getAbsolutePath(), mediaProcess.getBucket(), e.getMessage());
                      //保存任务处理失败的结果
                      mediaProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "处理后视频上传或入库失败");
                        //删除临时文件
                        if (mp4File != null) {
                            mp4File.delete();
                        }
                        if (minioFile != null) {
                            minioFile.delete();
                        }
                      return;
                  }

              });
          }catch (Exception e){
              log.error("处理任务失败,任务id:{}", mediaProcess.getId());
          }finally {
              countDownLatch.countDown();

          }

        });
        //等待,给一个充裕的超时时间,防止无限等待，到达超时时间还没有处理完成则结束任务
        countDownLatch.await(30, TimeUnit.MINUTES);

    }
    private String getFilePath(String fileMd5,String fileExt){
        return   fileMd5.substring(0,1) + "/" + fileMd5.substring(1,2) + "/" + fileMd5 + "/" +fileMd5 +fileExt;
    }
}
